package Schiffeversenken;

import javax.microedition.lcdui.*;

/**
 * Represents a ship on battleground
 */
public class Ship
{
    // members
    private Position _position;

    /**
     * value    0   : 1x2   ##
     *          1   : 1x3   ###
     *          2   : 1x4   ####
     */
    private int     _shipType;
    private int     _hits = 0;      // defines how often ship already hit by enemy, (if hits==length ? sunken = true)

    /**
     * value    true    : vertical
     *          false   : horizontal
     */
    private boolean _horizontal;

    private boolean _sunken;
    private boolean _placed;        // unused, maybe interesting for some checks
    private Image   _shipImage;     
    private Image   _shipImage_sunken;

    private Position[] _fields;     // contains fields ship lays on


    
    // constructors

    /**
     * Creates a defined ship at position zero
     * @param   <int>   ShipType <ul><li><b>0</b> : ##</li><li><b>1</b> : ###</li><li><b>2</b> : ####</li></ul>
     */
    public Ship(int shipType)
    {
        _shipType   = shipType;
        _position   = new Position(0, 0);


        Init();
    }
    /**
     * Creates a definied ship at defined position
     * @param   <int>       ShipType <ul><li><b>0</b> : ##</li><li><b>1</b> : ###</li><li><b>2</b> : ####</li></ul>
     * @param   <Position>  Position
     */
    public Ship(int shipType, Position pos)
    {
        _shipType   = shipType;
        _position   = pos;

        Init();
    }
    /**
     * Creates a definied ship at position describes by paramters
     * @param   <int>   ShipType <ul><li><b>0</b> : ##</li><li><b>1</b> : ###</li><li><b>2</b> : ####</li></ul>
     * @param   <int>   X-Coordinate
     * @param   <int>   Y-Coordinate
     */
    public Ship(int shipType, int x, int y)
    {
        _shipType   = shipType;
        _position   = new Position(x, y);

        Init();
    }


    // public methods

    /**
     * Check if this ship doesn't collide with an array of other ships.
     * @param   <Ship[]>    Array with all ships on battleground
     * @param   <int>       Index of this ship in shiparray to avoid it colliding with itself
     * @return  <boolean>   <b>true</b> position is valid or <b>false</b> position is invalid
     * @todo    * no ship-padding yet
     */
    public boolean isValidPosition(Ship[] _otherShips, int excludeIndex)
    {
        boolean chk = true;

        if( _position.X() < 0 || ( (_position.X()+this.getShipLength() > GameInstance.COLS && _horizontal) || (_position.X() > GameInstance.COLS && !_horizontal) ))
        {
            chk = false;
        }
        if( _position.Y() < 0 || ( (_position.Y()+this.getShipLength() > GameInstance.ROWS && !_horizontal) || (_position.Y() > GameInstance.ROWS && _horizontal) ))
        {
            chk = false;
        }

        for (int n = 0; n < _otherShips.length; n++)
        {
            for (int n2 = -1; n2 < getShipLength()+1; n2++)
            {
                if(_otherShips[n] != null && n != excludeIndex)
                {
                    if(_horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X()+n2, _position.Y())))
                        chk = false;
                    if(_position.Y()+1 < GameInstance.ROWS && _horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X()+n2, _position.Y()+1)))
                        chk = false;
                    if(_position.Y() > 0 && _horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X()+n2, _position.Y()-1)))
                        chk = false;

                    if(!_horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X(), _position.Y()+n2)))
                        chk = false;
                    if(_position.X()+1 < GameInstance.COLS && !_horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X()+1, _position.Y()+n2)))
                        chk = false;
                    if(_position.X() > 0 && !_horizontal && _otherShips[n].isHittingShipAt(new Position(_position.X()-1, _position.Y()+n2)))
                        chk = false;
                }
            }
        }
        return chk;
    }

    /**
     * Checks if this ship would be hit by a shot at defined Position. (It's also used in Ship::isValidPosition() - Method)
     * @param   <Position>  Specify position for check.
     * @return  <boolean>   <b>true</b> hits ship at position, <b>false</b> miss ship at this position
     */
    public boolean isHittingShipAt(Position pos)
    {
        boolean chk = false;

        if(pos.X() >= _position.X() && pos.Y() >= _position.Y())
        {
            if( _horizontal && pos.X()<_position.X()+getShipLength() && pos.Y() == _position.Y() )
            {
                chk = true;
            }

            if( !_horizontal && pos.Y()<_position.Y()+getShipLength() && pos.X() == _position.X() )
            {
                chk = true;
            }
        }

        return chk;
    }

    /**
     * Set this ship as placed.
     */
    public void placeShip()
    {
        _placed = true;
    }

    /**
     * Causes a hit to this ship. If it's hit enough it will sink.
     */
    public void Hit()
    {
        _hits++;
        if(_hits == getShipLength())
            _sunken = true;
    }




    // private methods

    /**
     * Initialize a new Ship
     */
    private void Init()
    {
        _sunken     = false;
        _horizontal = true;
        _placed     = false;

        redefineShipType();
        recalculateFields();
    }

    /**
     * Calculates the fields this ships lays by ships position, orientation and length
     */
    private void recalculateFields()
    {
        int length = getShipLength();
        _fields = new Position[length];

        _fields[0] = new Position();
        _fields[0] = _position;

        if(_horizontal)
        {
            for(int n=1; n<length; n++)
            {
                _fields[n] = new Position(_position.X()+n, _position.Y());
            }
        }
        else
        {
            for(int n=1; n<length; n++)
            {
                _fields[n] = new Position(_position.X(), _position.Y()+n);
            }
        }
    }


    /**
     * Redefine ships image by it's type and orientation
     */
    private void redefineShipType()
    {
        try
        {
            String dir = "h";
            if(!_horizontal)
                dir = "v";

            _shipImage = Image.createImage("/ship"+(_shipType+1)+"_"+dir+".png");
            _shipImage_sunken = Image.createImage("/ship"+(_shipType+1)+"_"+dir+"_sunk.png");
        } catch (Exception ex) {}
    }
    
     /**
      * Play Sound for hit the ship
      * @param   <int>  soundID <ul><li><b>0</b> : Hit</li><li><b>1</b> : Sunken</li></ul>
     */
    
    
    // properties

    /**
     * @return  <int>   Ships length
     */
    public int getShipLength()
    {
        return _shipType+2;
    }
    /**
     * @param   <boolean>   <b>true</b> ship orientates horizontal, <b>false</b> vertical
     */
    public void setOrientation(boolean horizontal)
    {
        _horizontal = horizontal;
        redefineShipType();
    }
    /**
     * Inverts the orientation of this ship
     */
    public void toggleOrientation()
    {
        if(_horizontal)
            _horizontal = false;
        else
            _horizontal = true;

        redefineShipType();
    }
    /**
     * @return  <boolean>   <b>true</b> if ship is orientated horizontal
     */
    public boolean isHorizontal()
    {
        return _horizontal;
    }
    /**
     * @return  <boolean>   <b>true</b> if ship is orientated vertical
     */
    public boolean isVertical()
    {
        return !_horizontal;
    }
    /**
     * @param   <Position>  Sets position on battleground
     */
    public void setPosition(Position pos)
    {
        _position = pos;
    }
    /**
     * @param   <int>   x-Coordinate
     * @param   <int>   y-Coordinate
     */
    public void setPosition(int x, int y)
    {
        _position = new Position(x, y);
    }
    /**
     * @return  <Position>  Position on battleground
     */
    public Position getPosition()
    {
        return _position;
    }
    /**
     * @return  <int>   Ships x-Coordinate
     */
    public int X()
    {
        return _position.X();
    }
    /**
     * @return  <int>   Ships y-Coordinate
     */
    public int Y()
    {
        return _position.Y();
    }
    /**
     * @return  <int>   x-position in pixels for position on screen
     */
    public int XinMatrix()
    {
        if(_horizontal)
            return (_position.X()) * (GameInstance.FIELDWIDTH+1) + 1/* + (int)(GameInstance.FIELDWIDTH/2)*/;
        else
            return (_position.X()) * (GameInstance.FIELDWIDTH+1)/* + (int)(GameInstance.FIELDWIDTH/4)*/;
    }
    /**
     * @return  <int>   y-position in pixels for position on screen
     */
    public int YinMatrix()
    {
        if(_horizontal)
            return (_position.Y()) * (GameInstance.FIELDWIDTH+1)/* + (int)(GameInstance.FIELDWIDTH/4)*/;
        else
            return (_position.Y()) * (GameInstance.FIELDWIDTH+1)+ 1/* + (int)(GameInstance.FIELDWIDTH/2)*/;
    }
    /**
     * Set type of ship
     * @param   <int>   0:##    1:###   2:####
     */
    public void setShipType(int shipType)
    {
        _shipType = shipType;
        redefineShipType();
        recalculateFields();
    }
    /**
     * Get type of ship
     * @return   <int>   0:##    1:###   2:####
     */
    public int getShipType()
    {
        return _shipType;
    }
    /**
     * @return  <boolean>   <b>true</b> if ship sunk
     */
    public boolean isSunken()
    {
        return _sunken;
    }
    /**
     * @return <Image>  ShipImage
     */
    public Image getImage()
    {
        int size = GameInstance.FIELDWIDTH;
        if (!this.isSunken())
        {
            if (this.isHorizontal())
            {
                return GameInstance.resizeTransparentImage(_shipImage, this.getShipLength()*size, size);
                //return GameInstance.resizeImage(_shipImage, this.getShipLength()*size, size);
            }
            else
            {
                return GameInstance.resizeTransparentImage(_shipImage, size, this.getShipLength()*size);
            }
        }
        else
        {
            if (this.isHorizontal())
            {
                return GameInstance.resizeTransparentImage(_shipImage_sunken, this.getShipLength()*size, size);
            }
            else
            {
                return GameInstance.resizeTransparentImage(_shipImage_sunken, size, this.getShipLength()*size);
            }
        }
    }
    /**
     * @return  <int>   get amount of hits
     */
    public int getHits()
    {
        return _hits;
    }

}
